package cn.qingyun.gis.modules.youshang.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.qingyun.gis.exception.CommonException;
import cn.qingyun.gis.modules.express.config.Exception.CustomExceptionHandler;
import cn.qingyun.gis.modules.express.constant.RedisContant;
import cn.qingyun.gis.modules.express.entity.ExpressRegion;
import cn.qingyun.gis.modules.express.mapper.ExpressRegionMapper;
import cn.qingyun.gis.modules.express.service.IExpressRegionService;
import cn.qingyun.gis.modules.express.util.SyncTool;
import cn.qingyun.gis.modules.init.system.constant.enums.RegionNumPermissionEnum;
import cn.qingyun.gis.modules.init.system.entity.SysLog;
import cn.qingyun.gis.modules.init.system.entity.SysUser;
import cn.qingyun.gis.modules.init.system.mapper.SysLogMapper;
import cn.qingyun.gis.modules.init.system.mapper.SysUserMapper;
import cn.qingyun.gis.modules.init.utils.CsvTool;
import cn.qingyun.gis.modules.init.utils.DictUtil;
import cn.qingyun.gis.modules.init.utils.JwtUtil;
import cn.qingyun.gis.modules.youshang.entity.ImportYsChannelCsvData;
import cn.qingyun.gis.modules.youshang.entity.YsChannelAttachment;
import cn.qingyun.gis.modules.youshang.entity.YsChannelProofread;
import cn.qingyun.gis.modules.youshang.entity.dto.*;
import cn.qingyun.gis.modules.youshang.mapper.YsChannelAttachmentMapper;
import cn.qingyun.gis.modules.youshang.mapper.YsChannelProofreadMapper;
import cn.qingyun.gis.modules.youshang.request.YsChannelRequest;
import cn.qingyun.gis.modules.youshang.request.YsProofreadRequest;
import cn.qingyun.gis.modules.youshang.request.YsQueryRequest;
import cn.qingyun.gis.modules.youshang.service.IYsChannelAttachmentService;
import cn.qingyun.gis.modules.youshang.service.IYsChannelProofreadService;
import cn.qingyun.gis.utils.SpringContextUtils;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import com.google.common.collect.Lists;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.io.WKTReader;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;


/**
 * <p>
 * 友商渠道勘误 服务实现类
 * </p>
 *
 * @author guest
 * @since 2023-09-12
 */
@Service
@Slf4j
public class YsChannelProofreadServiceImpl extends ServiceImpl<YsChannelProofreadMapper, YsChannelProofread> implements IYsChannelProofreadService {
    @Resource
    private YsChannelProofreadMapper ysChannelProofreadMapper;
    @Resource
    private YsChannelAttachmentMapper ysChannelAttachmentMapper;
    @Resource
    private IExpressRegionService iExpressRegionService;
    @Resource
    private IYsChannelAttachmentService iYsChannelAttachmentService;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private HttpServletResponse response;
    @Resource
    private CustomExceptionHandler customExceptionHandler;
    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private ExpressRegionMapper expressRegionMapper;
    @Resource
    private SysLogMapper sysLogMapper;

    @Value("${qingyun.exportPhoto.service-host:http://127.0.0.1:3000/#/}")
    private String exportHost;

    @Override
    @Transactional
    public void importYsChannelCsv0912(MultipartFile file) {
        try {
            long startTime = System.currentTimeMillis(); //获取当前内容
            CsvReader csvReader = new CsvReader(file.getInputStream(), StandardCharsets.UTF_8);
            // 读表头
            csvReader.setSafetySwitch(false);
            csvReader.readHeaders();
            List<ImportYsChannelCsvData> imports = Lists.newArrayList();
            // 读内容
            while (csvReader.readRecord()) {
                imports.add(CsvTool.toBean(csvReader, ImportYsChannelCsvData.class));
            }
            int batchSize = 1000; //批次处理数据量
            if (CollUtil.isNotEmpty(imports)) {
                // 高并发导入
                int count = imports.size();
                int pages = (count / batchSize) + 1; // 1000条数据启动一个线程

                SyncTool.doSync(pages, i -> {
                    int batchNum = i == 1 ? 0 : (i - 1) * batchSize; //开始索引
                    int batchEnd = Math.min(batchNum + batchSize, count); //结束索引 = batchEnd - 1
                    List<YsChannelProofread> recordsBatch = Lists.newArrayList(); //存放批次数据
                    for (int j = batchNum; j < batchEnd; j++) {
                        ImportYsChannelCsvData anImport = imports.get(j);
                        YsChannelProofread ysChannelProofread = new YsChannelProofread();
                        BeanUtils.copyProperties(anImport, ysChannelProofread);
                        //csv中的poi_name 属性为 友商渠道名称
                        ysChannelProofread.setProvince("山东");
                        ysChannelProofread.setYsChannelName(anImport.getPoiName());
                        ysChannelProofread.setLon(new BigDecimal(anImport.getLon()));
                        ysChannelProofread.setLat(new BigDecimal(anImport.getLat()));
                        ysChannelProofread.setKwStatus(0); //默认为未核实状态
                        ysChannelProofread.setDataVersion(1); //默认版本号从1开始
                        // 导入时间数据
                        ysChannelProofread.setCreateTime(DateUtil.date());
                        ysChannelProofread.setUpdateTime(DateUtil.date());
                        recordsBatch.add(ysChannelProofread);
                    }
                    this.saveBatch(recordsBatch);
                    recordsBatch.clear();
                });
            }
            log.debug("数据解析成功，当前导入需要花费{}毫秒", System.currentTimeMillis() - startTime);
        } catch (Exception e) {
            log.error("数据解析失败", e);
        }
    }

    @SneakyThrows
    @Override
    public void exportYsChannelCsv0912() {
        response.setContentType("application/csv; charset=utf-8");
        response.setHeader("Content-Disposition", "attachment; filename=" + new String("友商渠道规划.csv".getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
        CsvWriter csvWriter = new CsvWriter(response.getOutputStream(), ',', StandardCharsets.UTF_8);
        String[] fields = {"id", "ys_channel_name", "poi_lon", "poi_lat", "operate_state", "belong_operator", "channel_one_type", "channel_two_type", "house_type", "floor_space", "boss_name", "boss_phone", "poi_id",
                "province", "city", "district", "multi_addr_chn", "grid_id", "grid_name", "kw_status", "version",
                "poi_name", "city_code", "mirco_grid_id", "kw_phone", "mirco_grid_name", "category_ids", "category_names", "region_city", "region_districts", "region_city_code", "region_districts_code"};
        csvWriter.writeRecord(fields);
        int count = this.count();
        int pages = (count / 1000) + 1;
        //并发导入
        SyncTool.doSync(pages, i -> {
            Page<YsChannelProofread> page = new Page<>(i, 1000);
            List<YsChannelProofread> ysChannelProofreads = ysChannelProofreadMapper.selectPage(page, new QueryWrapper<>()).getRecords();
            for (YsChannelProofread row : ysChannelProofreads) {
                String[] values = {row.getId(), row.getYsChannelName(), row.getLon().toString(), row.getLat().toString(), row.getOperateState(), row.getBelongOperator(),
                        row.getChannelOneType(), row.getChannelTwoType(), row.getHouseType(), row.getFloorSpace(), row.getBossName(), row.getBossPhone(), row.getPoiId(),
                        row.getProvince(), row.getCity(), row.getDistrict(), row.getMultiAddrChn(), row.getGridId(), row.getGridName(), String.valueOf(row.getKwStatus()),
                        String.valueOf(row.getDataVersion()),
                        row.getPoiName(), row.getCityCode(), row.getMircoGridId(), row.getKwPhone(), row.getMircoGridName(), row.getCategoryIds(), row.getCategoryNames(),
                        row.getRegionCity(), row.getRegionDistricts(), row.getRegionCityCode(), row.getRegionDistrictsCode()
                };
                synchronized (csvWriter) {
                    try {
                        csvWriter.writeRecord(values);
                    } catch (IOException e) {
                        throw new CommonException("写入失败");
                    }
                }
            }
        });
        log.debug("已成功导出{}条友商渠道数据", this.count());
        csvWriter.close();
    }

    @Override
    public void formattingYsChannelCsv0912() {
        this.remove(new QueryWrapper<>()); // 进行全表格式化
        ysChannelAttachmentMapper.delete(new QueryWrapper<>());// 进行附表格式化
    }

    @Override
    public IPage<YsChannelProofread> queryList(YsQueryRequest request) {
        if (CharSequenceUtil.isNotEmpty(request.getGridType()) &&
                Boolean.TRUE.equals(RegionNumPermissionEnum.pitchRegion(request.getGridType()))) {
            // 创建分页对象
            IPage<YsChannelProofread> page = new Page<>(request.getPageNum(), request.getPageSize());
            //根据网格编码+工号+未勘误（勘误状态=0）+校对类型+修改时间降序匹配查询
            QueryWrapper<YsChannelProofread> queryWrapper = new QueryWrapper<>();
            if (ObjectUtil.isNotEmpty(request.getLat()) && ObjectUtil.isNotEmpty(request.getLon())) {
                queryWrapper.select("ys_channel_proofread.*, " +
                                String.format("round(ST_Distance_Sphere(POINT(%s, %s), POINT(lon, lat))/1000,2) AS distance", request.getLon(), request.getLat()))
                        .orderByAsc("distance");
            }
            queryWrapper.lambda()
                    .eq(YsChannelProofread::getKwStatus, 0)
                    .orderByDesc(YsChannelProofread::getUpdateTime);
            if (ObjectUtil.isNotEmpty(request.getKeyword())) {
                queryWrapper.lambda().and(
                        lwp -> lwp.like(YsChannelProofread::getPoiName, request.getKeyword()).
                                or().
                                like(YsChannelProofread::getMultiAddrChn, request.getKeyword())
                );
            }

            // 对前端网格列表进行处理
            String[] gridIdStr = request.getGridId().split(",");
            List<String> gridIdList = Arrays.asList(gridIdStr);
            // 工号为省级工号
            if (RegionNumPermissionEnum.PROVINCE.name().equals(request.getGridType()) && gridIdList.size() == 1) {
                return ysChannelProofreadMapper.selectPage(page, queryWrapper);
            } else {
                queryWrapper.lambda().
                        and(
                                wp -> wp.in(YsChannelProofread::getRegionCityCode, gridIdList).
                                        or().
                                        in(YsChannelProofread::getRegionDistrictsCode, gridIdList).
                                        or().
                                        in(YsChannelProofread::getGridId, gridIdList)
                        );
                return ysChannelProofreadMapper.selectPage(page, queryWrapper);
            }
        }

        return new Page<>();

    }

    @Override
    @Transactional
    public String ysProofread(YsProofreadRequest request) {
        RLock rlock = redissonClient.getLock(RedisContant.YS_CHANNEL_PROOF_LOCK);
        try {
            boolean lockRes = rlock.tryLock(10, TimeUnit.SECONDS);
            if (lockRes) {
                //1.处理ys_channel_proofread表
                YsChannelProofread proofread = ysChannelProofreadMapper.selectById(request.getId());
                BeanUtil.copyProperties(request, proofread);
                proofread.setKwStatus(1); //更新勘误状态
                proofread.setUpdateTime(DateUtil.date());//更新修改(勘误)时间
                ysChannelProofreadMapper.updateById(proofread);
                //2.图片上传
                iYsChannelAttachmentService.upload(request.getPictureList(), request.getId());
                return "勘误成功";
            }
            return "系统繁忙，请稍后再试";
        } catch (Exception e) {
            throw new CommonException("勘误失败");
        } finally {
            rlock.unlock(); // 释放redis锁
        }
    }

    @Override
    public IPage<YsChannelProofread> queryTotal(YsQueryRequest request) {
        if (CharSequenceUtil.isNotEmpty(request.getGridType()) && RegionNumPermissionEnum.pitchRegion(request.getGridType())) {
            QueryWrapper<YsChannelProofread> queryWrapper = new QueryWrapper<>();
            if (ObjectUtil.isNotEmpty(request.getLat()) && ObjectUtil.isNotEmpty(request.getLon())) {
                queryWrapper.select("ys_channel_proofread.*, " +
                                String.format("round(ST_Distance_Sphere(POINT(%s, %s), POINT(lon, lat))/1000,2) AS distance", request.getLon(), request.getLat()))
                        .orderByAsc("distance");
            }
            queryWrapper.lambda().eq(YsChannelProofread::getKwStatus, 1).orderByDesc(YsChannelProofread::getUpdateTime);

            String currentSurveyStartTime = DictUtil.getDictItemValueByName("YSCurrentSurveyTime", "YSCurrentSurveyStartTime");
            if (StringUtils.isNotEmpty(currentSurveyStartTime)){
                DateTime beginTime = DateUtil.parse(currentSurveyStartTime, DatePattern.NORM_DATETIME_FORMATTER);
                queryWrapper.lambda().ge(YsChannelProofread::getUpdateTime, beginTime);
            }

            //多网格查询
            String[] gridIdStr = request.getGridId().split(",");
            List<String> gridIdList = Arrays.asList(gridIdStr);

            //创建分页对象
            IPage<YsChannelProofread> page = new Page<>(request.getPageNum(), request.getPageSize());
            // 运营商查询
            if (ObjectUtil.isNotEmpty(request.getBelongOperator())) {
                queryWrapper.lambda().like(YsChannelProofread::getBelongOperator, request.getBelongOperator());
            }

            // 运营状态查询
            if (ObjectUtil.isNotEmpty(request.getOperateState())) {
                queryWrapper.lambda().eq(YsChannelProofread::getOperateState, request.getOperateState());
            }
            if (ObjectUtil.isNotEmpty(request.getKeyword())) {
                queryWrapper.lambda().and(
                        lwp -> lwp.like(YsChannelProofread::getPoiName, request.getKeyword()).
                                or().
                                like(YsChannelProofread::getYsChannelName, request.getKeyword()).
                                or().
                                like(YsChannelProofread::getMultiAddrChn, request.getKeyword())
                );
            }
            // 工号为省级工号
            if (RegionNumPermissionEnum.PROVINCE.name().equals(request.getGridType()) && gridIdList.size() == 1) {
                return ysChannelProofreadMapper.selectPage(page, queryWrapper);
            } else {
                queryWrapper.lambda().
                        and(
                                wp -> wp.in(YsChannelProofread::getRegionCityCode, gridIdList).
                                        or().
                                        in(YsChannelProofread::getRegionDistrictsCode, gridIdList).
                                        or().
                                        in(YsChannelProofread::getGridId, gridIdList)
                        );
                return ysChannelProofreadMapper.selectPage(page, queryWrapper);
            }
        }
        return new Page<>();
    }

    @Override
    public void exportYsChannelTotal(String cachePath, String fileName, String exportWay) {
        response.setContentType("application/vnd.ms-excel");
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));

        // 声明条件
        QueryWrapper<YsChannelProofread> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().
                eq(YsChannelProofread::getKwStatus, 1).
                le(YsChannelProofread::getUpdateTime, DateUtil.date()).
                ge(YsChannelProofread::getUpdateTime, DateUtil.yesterday());

        // 在数据库中获取友商已勘误增量校对信息
        List<YsChannelProofread> ysChannelProofreadList = ysChannelProofreadMapper.selectList(queryWrapper);
        List<ExportYsChannelDto> exportYsChannelDtoList = new ArrayList<>();

        for (YsChannelProofread data : ysChannelProofreadList) {
            ExportYsChannelDto exportYsChannelDto = new ExportYsChannelDto();
            BeanUtil.copyProperties(data, exportYsChannelDto);
            exportYsChannelDto.setUpdateTime(DateUtil.format(data.getUpdateTime(), "yyyy-MM-dd HH:mm:ss")); // 将Date时间类型 -> String
            exportYsChannelDto.setPointStatue("有效");
            // 渠道地址 = 归属省份 + 地市 + 区县 + 详细地址
            exportYsChannelDto.setYsChannelAddress(data.getProvince() + data.getCity() + data.getDistrict() + data.getMultiAddrChn());
            // 从附件表中获取附件中图片base64字符串
            List<YsChannelAttachment> ysChannelAttachmentList = iYsChannelAttachmentService.show(data.getId());
            if (!ysChannelAttachmentList.isEmpty()) {
                if (ysChannelAttachmentList.size() == 1) { // 存在1张图片
                    exportYsChannelDto.setChannelPictureOne(ysChannelAttachmentList.get(0).getContent());
                }
                if (ysChannelAttachmentList.size() == 2) { // 存在2张图片
                    exportYsChannelDto.setChannelPictureOne(ysChannelAttachmentList.get(0).getContent());
                    exportYsChannelDto.setChannelPictureTwo(ysChannelAttachmentList.get(1).getContent());
                }
                if (ysChannelAttachmentList.size() == 3) { // 存在3张图片
                    exportYsChannelDto.setChannelPictureOne(ysChannelAttachmentList.get(0).getContent());
                    exportYsChannelDto.setChannelPictureTwo(ysChannelAttachmentList.get(1).getContent());
                    exportYsChannelDto.setChannelPictureThree(ysChannelAttachmentList.get(2).getContent());
                }
            }
            exportYsChannelDtoList.add(exportYsChannelDto);
        }
        if ("sftp".equals(exportWay)) {
            try (ExcelWriter excelWriter = EasyExcelFactory.write(new FileOutputStream(cachePath + fileName)).build()) {
                WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "增量数据").head(ExportYsChannelDto.class).build();
                excelWriter.write(exportYsChannelDtoList, writeSheet);
                log.debug("2-缓存至\\tmp\\" + fileName + "成功！");
            } catch (FileNotFoundException e) {
                log.error("2-缓存至\\tmp\\" + fileName + "失败！");
                throw new CommonException("缓存友商渠道增量数据失败！" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss"));
            }
        } else {
            try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
                WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "增量数据").head(ExportYsChannelDto.class).build();
                excelWriter.write(exportYsChannelDtoList, writeSheet);
                log.debug("在线导出" + fileName + "成功！");
            } catch (Exception e) {
                log.error("在线导出" + fileName + "失败！");
                throw new CommonException("在线导出友商渠道增量数据失败！---" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss"));
            }
        }

    }

    @Override
    @Transactional
    public void matchGrid() {
        AtomicInteger n = new AtomicInteger();
        // 获取微网格级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> miroGridWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "5").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> miroGridTotal = iExpressRegionService.list(miroGridWrapper);
        // 获取网格级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> gridWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "4").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> gridTotal = iExpressRegionService.list(gridWrapper);
        // 获取区县级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> districtsWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "3").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> districtsTotal = iExpressRegionService.list(districtsWrapper);
        // 获取地市级别业务区划数据
        LambdaQueryWrapper<ExpressRegion> cityWrapper = new QueryWrapper<ExpressRegion>().lambda().eq(ExpressRegion::getLevel, "2").ne(ExpressRegion::getGeometry, "");
        List<ExpressRegion> cityTotal = iExpressRegionService.list(cityWrapper);

        // 计算全量友商信息数据条数
        int count = this.count();
        int pages = (count / 1000) + 1;
        long start = System.currentTimeMillis();

        // 根据数据总量划分数据块并启动多线程
        SyncTool.doSync(pages, i -> {
            Page<YsChannelProofread> page = new Page<>(i, 1000);
            List<YsChannelProofread> proofreads = this.page(page, null).getRecords();
            List<YsChannelProofread> updateRecords = Lists.newArrayList();

            for (YsChannelProofread data : proofreads) {
                try {
                    log.debug(String.format("====================开始匹配 %s====================", data.getId()));
                    // 如果站点经纬度都不为空或空字符串
                    if (ObjectUtil.isNotEmpty(data.getLon()) && ObjectUtil.isNotEmpty(data.getLat())) {
                        // 创建点对象
                        Point point = new GeometryFactory().createPoint(new Coordinate(data.getLon().doubleValue(), data.getLat().doubleValue()));
                        // 创建边界对象
                        Geometry geometry = null;
                        // 1.进行微网格匹配
                        for (ExpressRegion miroGridData : miroGridTotal) {
                            // 创建面对象
                            geometry = new WKTReader().read(miroGridData.getGeometry());
                            // 微网格匹配成功
                            if (geometry.contains(point)) {
                                data.setMircoGridId(miroGridData.getCode());
                                data.setMircoGridName(miroGridData.getName());
                                break;
                            }
                        }
                        // 2.进行网格匹配
                        for (ExpressRegion gridData : gridTotal) {
                            // 创建面对象
                            geometry = new WKTReader().read(gridData.getGeometry());
                            if (geometry.contains(point)) {
                                data.setGridId(gridData.getCode());
                                data.setGridName(gridData.getName());
                                break;
                            }
                        }
                        // 3.进行区县匹配
                        for (ExpressRegion disData : districtsTotal) {
                            geometry = new WKTReader().read(disData.getGeometry());
                            if (geometry.contains(point)) {
                                data.setRegionDistrictsCode(disData.getCode());
                                data.setRegionDistricts(disData.getName());
                                break;
                            }

                        }
                        // 4.进行地市匹配
                        for (ExpressRegion cityData : cityTotal) {
                            // 创建面对象
                            geometry = new WKTReader().read(cityData.getGeometry());
                            // 匹配地市
                            if (geometry.contains(point)) {
                                data.setRegionCityCode(cityData.getCode());
                                data.setRegionCity(cityData.getName());
                                break;
                            }
                        }
                    }
                    synchronized (this) {
                        n.incrementAndGet();
                        log.debug(String.format("=========== 当前匹配进度为%s / %s ===========", n, count));
                    }
                    updateRecords.add(data);
                } catch (Exception e) {
                    customExceptionHandler.handleException(String.format("友商站点-%s,入格失败！|报错：%s", data.getYsChannelName(), e.getMessage()), start);
                }
            }
            // 批量导入匹配之后数据
            this.updateBatchById(updateRecords);

        });
        log.debug(String.format("匹配网格完成，====一共处理%s快递数据====", count));
        // 加入日志
        SysLog sysLog = new SysLog();
        long finish = System.currentTimeMillis();
        sysLog.setLogContent("友商匹配数据结束");
        sysLog.setCreateTime(DateUtil.date());
        sysLog.setLogType(3);
        sysLog.setCostTime(finish - start);
        sysLog.setCreateBy("system");
        sysLog.setUserid("匹配任务");
        sysLogMapper.insert(sysLog);
    }

    @SneakyThrows
    @Override
    public void exportYsChannelExcel(HttpServletRequest request) {
        response.setContentType("application/vnd.ms-excel");
        String fileName = "友商渠道明细表";
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));

        //1. 根据工号进行汇总
        String currentUserName = JwtUtil.getUserNameByToken(request);
        // 根据用户信息查询区域编码
        SysUser user = sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().
                select(SysUser::getRegionCode).
                eq(SysUser::getUsername, currentUserName));
        log.debug("当前登录用户信息：{} 以及相应配置权限编码{}", currentUserName, user.getRegionCode());
        // 根据区域编码判断当前工号区域权限
        QueryWrapper<ExpressRegion> regionQueryWrapper = new QueryWrapper<>();
        regionQueryWrapper.lambda().select(ExpressRegion::getLevel, ExpressRegion::getCode).eq(ExpressRegion::getCode, user.getRegionCode());
        ExpressRegion res = expressRegionMapper.selectOne(regionQueryWrapper);

        List<ExportChannelRecordDto> exportChannelRecordDtos = Lists.newArrayList();

        String currentSurveyStartTime = DictUtil.getDictItemValueByName("YSCurrentSurveyTime", "YSCurrentSurveyStartTime");

        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            // 当前省级配置工号
            if ("0000SD".equals(user.getRegionCode())) {
                exportChannelRecordDtos = ysChannelProofreadMapper.selectAll("1", null,currentSurveyStartTime);
            } else if (ObjectUtil.isNotEmpty(res.getLevel()) && ObjectUtil.isNotEmpty(res.getCode())) {
                exportChannelRecordDtos = ysChannelProofreadMapper.selectAll(res.getLevel(), res.getCode(),currentSurveyStartTime);
            }

            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "友商渠道汇总数据").head(ExportChannelRecordDto.class).build();
            excelWriter.write(exportChannelRecordDtos, writeSheet);

            QueryWrapper<YsChannelProofread> ysChannelProofreadQueryWrapper = new QueryWrapper<>();
            if (!"0000SD".equals(user.getRegionCode())) { // 并非省级工号
                if ("2".equals(res.getLevel())) {
                    ysChannelProofreadQueryWrapper.lambda().eq(YsChannelProofread::getRegionCityCode, res.getCode());
                }
                if ("3".equals(res.getLevel())) {
                    ysChannelProofreadQueryWrapper.lambda().eq(YsChannelProofread::getRegionDistrictsCode, res.getCode());
                }
                if ("4".equals(res.getLevel())) {
                    ysChannelProofreadQueryWrapper.lambda().eq(YsChannelProofread::getGridId, res.getCode());
                }
            }

            if (StringUtils.isNotEmpty(currentSurveyStartTime)) {
                DateTime beginTime = DateUtil.parse(currentSurveyStartTime, DatePattern.NORM_DATETIME_FORMATTER);
                ysChannelProofreadQueryWrapper.lambda().ge(YsChannelProofread::getUpdateTime, beginTime);
            }

            int batchSize = 2000;
            int count = this.count(ysChannelProofreadQueryWrapper);
            int pages = (count / batchSize) + 1;

            WriteSheet detailSheet = EasyExcelFactory.writerSheet(1, "友商渠道全量明细数据").head(ExportYsChannelTotalDto.class).build();
            SyncTool.doSync(pages, i -> {
                Page<YsChannelProofread> page = new Page<>(i, batchSize);
                List<YsChannelProofread> ysChannelProofreadList = ysChannelProofreadMapper.selectPage(page, ysChannelProofreadQueryWrapper).getRecords();
                List<ExportYsChannelTotalDto> exportYsChannelDtoList = new ArrayList<>();
                for (YsChannelProofread data : ysChannelProofreadList) {
                    ExportYsChannelTotalDto exportYsChannelDto = new ExportYsChannelTotalDto();
                    BeanUtil.copyProperties(data, exportYsChannelDto);
                    // 渠道地址 = 归属省份 + 地市 + 区县 + 详细地址
                    exportYsChannelDto.setYsChannelAddress(data.getProvince() + data.getCity() + data.getDistrict() + data.getMultiAddrChn());
                    // 时间类型转换
                    exportYsChannelDto.setUpdateTime(DateUtil.format(data.getUpdateTime(), "yyyy-MM-dd HH:mm:ss"));
                    exportYsChannelDtoList.add(exportYsChannelDto);
                }
                synchronized (excelWriter) {
                    excelWriter.write(exportYsChannelDtoList, detailSheet);
                }
            });
        }
    }

    @Override
    public IPage<ExportChannelRecordDto> analysisDataList(HttpServletRequest request, Page<ExportChannelRecordDto> page) {
        //1. 根据工号进行汇总
        String currentUserName = JwtUtil.getUserNameByToken(request);
        // 根据用户信息查询区域编码
        SysUser user = sysUserMapper.selectOne(new QueryWrapper<SysUser>().lambda().
                select(SysUser::getRegionCode).
                eq(SysUser::getUsername, currentUserName));
        log.debug("当前登录用户信息：{} 以及相应配置权限编码{}", currentUserName, user.getRegionCode());

        // 根据区域编码判断当前工号区域权限
        QueryWrapper<ExpressRegion> regionQueryWrapper = new QueryWrapper<>();
        regionQueryWrapper.lambda().select(ExpressRegion::getLevel, ExpressRegion::getCode).eq(ExpressRegion::getCode, user.getRegionCode());
        ExpressRegion res = expressRegionMapper.selectOne(regionQueryWrapper);

        IPage<ExportChannelRecordDto> exportChannelRecordDtos = new Page<>();
        // 当前省级配置工号
        if ("0000SD".equals(user.getRegionCode())) {
            exportChannelRecordDtos = ysChannelProofreadMapper.selectAllPage(page, "1", null);
        } else if (ObjectUtil.isNotEmpty(res.getLevel()) && ObjectUtil.isNotEmpty(res.getCode())) {
            exportChannelRecordDtos = ysChannelProofreadMapper.selectAllPage(page, res.getLevel(), res.getCode());
        }
        return exportChannelRecordDtos;
    }

    @Override
    public void addYsChannel(YsChannelRequest ysChannelRequest) {
        // 进行入格
        if (ObjectUtil.isEmpty(ysChannelRequest.getLat()) || ObjectUtil.isEmpty(ysChannelRequest.getLon())) {
            throw new CommonException("入参经纬度字段不能为空");
        }
        // 进行微网格匹配
        RegionDto regionDto = ysChannelProofreadMapper.pointPitchMiroGrid(ysChannelRequest.getLon(), ysChannelRequest.getLat());
        BeanUtils.copyProperties(regionDto, ysChannelRequest);
        // 微网格匹配失败，进行网格匹配
        if (ObjectUtil.isEmpty(regionDto.getMircoGridId())) {
            regionDto = ysChannelProofreadMapper.pointPitchGrid(ysChannelRequest.getLon(), ysChannelRequest.getLat());
            BeanUtils.copyProperties(regionDto, ysChannelRequest);
        }
        // 网格匹配失败，进行区县匹配
        if (ObjectUtil.isEmpty(regionDto.getGridId())) {
            regionDto = ysChannelProofreadMapper.pointPitchDis(ysChannelRequest.getLon(), ysChannelRequest.getLat());
            BeanUtils.copyProperties(regionDto, ysChannelRequest);
        }
        // 区县匹配失败，进行地市匹配
        if (ObjectUtil.isEmpty(regionDto.getRegionDistrictsCode())) {
            regionDto = ysChannelProofreadMapper.pointPitchCity(ysChannelRequest.getLon(), ysChannelRequest.getLat());
            BeanUtils.copyProperties(regionDto, ysChannelRequest);
        }

        ysChannelRequest.setCreateTime(new Date());
        ysChannelRequest.setKwStatus(1); // 新增渠道 设置为 已校对
        ysChannelRequest.setDataVersion(10); // 设置数据版本为10 - 新增渠道情况
        if (!this.save(ysChannelRequest)) {
            throw new CommonException("添加渠道失败！");
        }

        // 图片上传
        if (!CollectionUtils.isEmpty(ysChannelRequest.getPictureList())) {
            iYsChannelAttachmentService.upload(ysChannelRequest.getPictureList(), ysChannelRequest.getId());
        }
    }

    @SneakyThrows
    @Override
    public void exportYsPhotos() {
//        String fileName = System.getProperty("user.dir") + "\\友商渠道关门营业门头照.xlsx";
        response.setContentType("application/vnd.ms-excel");
        String fileName = "友商渠道门头照片明细表";
        response.setHeader("content-disposition", String.format("attachment;filename=%s.xlsx", new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1) + DateUtil.now()));
        try (ExcelWriter excelWriter = EasyExcelFactory.write(response.getOutputStream()).build()) {
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(0, "全量数据").head(ExportYsPhotoDto.class).build();

//            QueryWrapper<YsChannelProofread> ysChannelProofreadQueryWrapper = new QueryWrapper<>();
//            ysChannelProofreadQueryWrapper.lambda().eq(YsChannelProofread::getKwStatus, 1);
            int batchSize = 1000;
            int count = this.count(null);
            int pages = (count / batchSize) + 1;

            SyncTool.doSync(pages, i -> {
                Page<YsChannelProofread> page = new Page<>(i, batchSize);
                List<YsChannelProofread> ysChannelProofreadList = ysChannelProofreadMapper.selectPage(page, null).getRecords();
                List<ExportYsPhotoDto> exportYsChannelDtoList = ysChannelProofreadList.stream().map(
                        item -> {
                            ExportYsPhotoDto dto = new ExportYsPhotoDto();
                            BeanUtils.copyProperties(item, dto);
                            dto.setYsChannelAddress(item.getProvince() + item.getCity() + item.getDistrict() + item.getMultiAddrChn());
                            // 获取门头照片链接
                            if (!iYsChannelAttachmentService.show(item.getId()).isEmpty()) {
                                dto.setChannelPicture(exportHost + "display?id=" + item.getId());
                            }
                            return dto;
                        }
                ).collect(Collectors.toList());
                synchronized (excelWriter) {
                    excelWriter.write(exportYsChannelDtoList, writeSheet);
                }
            });
        } catch (Exception e) {
            throw new CommonException("解析excel数据失败", e);
        }

    }

    @Override
    @Transactional
    public int deleteByPoiId(MultipartFile file) {
        List<String> idList = Lists.newArrayList();
        try {
            EasyExcelFactory.read(file.getInputStream(), ImportYsChannelProofreadDto.class, new ReadListener<ImportYsChannelProofreadDto>() {
                @Override
                public void onException(Exception exception, AnalysisContext context) {
                    if (exception instanceof ExcelDataConvertException) {
                        log.error("解析异常, 行：{}", ((ExcelDataConvertException) exception).getRowIndex());
                    } else {
                        log.error("解析异常, {}", exception.getClass().getName());
                    }
                }

                @Override
                public void invoke(ImportYsChannelProofreadDto data, AnalysisContext context) {
                    idList.add(data.getPoiId());
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {
                    IYsChannelProofreadService iYsChannelProofreadService = SpringContextUtils.getBean(IYsChannelProofreadService.class);
                    iYsChannelProofreadService.remove(new QueryWrapper<YsChannelProofread>().lambda().in(YsChannelProofread::getPoiId, idList));
                }
            }).sheet().doReadSync();
        } catch (Exception e) {
            log.error("根据poiId删除数据失败", e);
            throw new CommonException("根据poiId删除数据失败");
        }
        return idList.size();
    }

    @Override
    @Transactional
    public int updateByPoiId(MultipartFile file) {
        List<String> idList = Lists.newArrayList();
        try {
            EasyExcelFactory.read(file.getInputStream(), ImportYsChannelProofreadDto.class, new ReadListener<ImportYsChannelProofreadDto>() {
                @Override
                public void onException(Exception exception, AnalysisContext context) {
                    if (exception instanceof ExcelDataConvertException) {
                        log.error("解析异常, 行：{}", ((ExcelDataConvertException) exception).getRowIndex());
                    } else {
                        log.error("解析异常, {}", exception.getClass().getName());
                    }
                }

                @Override
                public void invoke(ImportYsChannelProofreadDto data, AnalysisContext context) {
                    String poiId = data.getPoiId();
//                    QueryWrapper<YsChannelProofread> queryWrapper = new QueryWrapper<>();
//                    queryWrapper.lambda().select(YsChannelProofread::getId).eq(YsChannelProofread::getPoiId, poiId);
//                    YsChannelProofread ysChannelProofread = ysChannelProofreadMapper.selectOne(queryWrapper);
//                    if (Objects.nonNull(ysChannelProofread)) {
//                        QueryWrapper<YsChannelAttachment> attachmentQueryWrapper = new QueryWrapper<>();
//                        attachmentQueryWrapper.lambda().select(YsChannelAttachment::getId, YsChannelAttachment::getYsChannelProofreadId).eq(YsChannelAttachment::getYsChannelProofreadId, ysChannelProofread.getId());
//                        List<YsChannelAttachment> ysChannelAttachments = ysChannelAttachmentMapper.selectList(attachmentQueryWrapper);
//                        if (CollUtil.isNotEmpty(ysChannelAttachments)) {
//                            for (YsChannelAttachment ysChannelAttachment : ysChannelAttachments) {
//                                UpdateWrapper<YsChannelAttachment> updateWrapper = new UpdateWrapper<>();
//                                updateWrapper.lambda().set(YsChannelAttachment::getYsChannelProofreadId, StrUtil.reverse(ysChannelAttachment.getYsChannelProofreadId())).eq(YsChannelAttachment::getId, ysChannelAttachment.getId());
//                                iYsChannelAttachmentService.update(updateWrapper);
//                            }
//                        }
//                    }
                    idList.add(poiId);
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {
                    IYsChannelProofreadService iYsChannelProofreadService = SpringContextUtils.getBean(IYsChannelProofreadService.class);
                    UpdateWrapper<YsChannelProofread> updateWrapper = new UpdateWrapper<>();
                    updateWrapper.lambda().set(YsChannelProofread::getKwStatus, 0).set(YsChannelProofread::getOperateState, "").set(YsChannelProofread::getBelongOperator, "").set(YsChannelProofread::getChannelOneType, "").set(YsChannelProofread::getHouseType, "").set(YsChannelProofread::getFloorSpace, "").set(YsChannelProofread::getBossName, "").set(YsChannelProofread::getBossPhone, "").set(YsChannelProofread::getChannelTwoType, "").set(YsChannelProofread::getKwPhone, "").in(YsChannelProofread::getPoiId, idList);
                    iYsChannelProofreadService.update(updateWrapper);
                }
            }).sheet().doReadSync();
        } catch (Exception e) {
            log.error("根据poiId更新数据失败", e);
            throw new CommonException("根据poiId更新数据失败");
        }
        return idList.size();
    }


}
